package experiment;

import heuristics.*;
import Entities.*;
import algorithms.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import log.Log;

public class CourseNumberComparingExperiment extends BaseExperiment {

	final static String[] logLabels = { "Number of Courses In Calendar", "Heuristic", "Algorithm",
			 "Average Heuristic Value", "Average Number of Heuristic Calls to Best" };
	final static int MAX_HEURISTIC_CALLS = 50000;
	final static int[] numberOfCoursesInCalendar = { 20, 50, 100, 150, 200 };
	final static String logPath = "experiments_output/CourseNumberComparingExperiment.csv";
	static List<IHeuristic> heuristics;
	static List<IAlgorithm> algorithms;
	static Log log;

	public CourseNumberComparingExperiment() {
		try {
			log = new Log(logPath);
			log.writeLabels(logLabels);
		} catch (IOException e) {
			e.printStackTrace();
		}
		heuristics = new LinkedList<IHeuristic>();
		heuristics.add(new LazyStudentHeuristic());
		heuristics.add(new AmbitiousStudentHeuristic());
		heuristics.add(new WorkingStudentHeuristic());

		algorithms = new LinkedList<IAlgorithm>();
		algorithms.add(new HillClimbing());
		algorithms.add(new RandomRestartHC());
		algorithms.add(new RandomWalkHC());

		// init Algorithms parameters
		RandomRestartHC.setSizeOfRandomCalendar(10);
		RandomWalkHC.setTypeOfRandomWalk(TYPE_OF_RANDOM_WALK.FIXED);
		RandomWalkHC.setFixedNumberOfRandomWalk(3);
	}

	@Override
	public void Execute() {
		System.out.println("Experiment Started: " + getDateTime());// TODO: debug
		HashMap<String, String> data = new HashMap<String, String>();
		try {
			for (int numberOfCourses : numberOfCoursesInCalendar) 
			{ // for each number of course
				setCourseConfigurations("instances" + String.valueOf(numberOfCourses) + "courses");
				data.put("Number of Courses In Calendar", String.valueOf(numberOfCourses));
					for (IAlgorithm algorithm : algorithms) 
					{
						algorithm.setMaxOfHeuristicCalls(MAX_HEURISTIC_CALLS);
						data.put("Algorithm", algorithm.toString());
						for (IHeuristic heuristic : heuristics) 
						{
							Calendar.setHeuristic(heuristic);
							data.put("Heuristic", heuristic.toString());
							RunExperiment(algorithm, data);
							log.writeData(data);
							for (String type : data.keySet()) { // TODO: debug
								System.out.println(type + ": " + data.get(type));
							}
							data.clear();
					}					
				}				
			}
			log.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	private void RunExperiment(IAlgorithm algorithm, HashMap<String, String> data) {
		Calendar res;
		double averageHeuristicValue = 0, averageNumberOfHeuristicCallsToBest = 0;

		for (CoursesConfiguration courseConfiguration : courseConfigurations) 
		{
			Calendar.init(courseConfiguration.getCoursesConfiguration());
			for (int runNum = 0 ; runNum < NUMBER_OF_REPETED_EXECUTIONS ; runNum++){
				algorithm.reset(); // reset variables in algorithm
				res = algorithm.searchLocal();
				averageHeuristicValue += res.getHeuristicValue();
				averageNumberOfHeuristicCallsToBest += algorithm.getNumberOfHeuristicCallsToBest();
			}
		}
		averageHeuristicValue /= (double) (NUMBER_OF_REPETED_EXECUTIONS*courseConfigurations.size());
		averageNumberOfHeuristicCallsToBest /= (double) (NUMBER_OF_REPETED_EXECUTIONS*courseConfigurations.size());
		data.put("Average Heuristic Value", String.valueOf(averageHeuristicValue));
		data.put("Average Number of Heuristic Calls to Best", String.valueOf(averageNumberOfHeuristicCallsToBest));
	}
}
